﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TeachMe.Application.Data;
using TeachMe.Application.Exceptions;
using TeachMe.Application.Forms;
using TeachMe.Application.Properties;

namespace TeachMe.Application
{
    public class ContextMenusManager
    {
        private readonly Dictionary<ContextMenuType, ContextMenuStrip> _menus = new Dictionary<ContextMenuType, ContextMenuStrip>();
        private readonly Root _root;
        private EntryDescriptor _entryDescriptor;
        private int _openedContextMenusCounter;

        public ContextMenusManager(Root root)
        {
            _root = root;
            _root.UserSettings.Language.Changed += delegate { _menus.Clear(); };
        }

        public bool ContextMenuOpened
        {
            get { return _openedContextMenusCounter > 0; }
        }

        public ContextMenuStrip Get(ContextMenuType type)
        {
            if (!_menus.ContainsKey(type))
            {
                _menus.Add(type, new ContextMenuStrip());

                _menus[type].Opening += OnContextMenuOpening;
                _menus[type].Closed += OnContextMenuClosed;

                switch (type)
                {
                    case ContextMenuType.Tray:
                        AddContextMenuAction(type, Resources.MenuItemCreateEntry, ContextMenuActionType.CreateEntry);
                        AddContextMenuAction(type, Resources.MenuItemDisplayEntry, ContextMenuActionType.DisplayEntry);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemSettings, ContextMenuActionType.Settings);
                        AddContextMenuAction(type, Resources.MenuItemTurboMode, ContextMenuActionType.TurboMode, true);
                        AddContextMenuAction(type, Resources.MenuItemSuspend, ContextMenuActionType.Suspend);
                        AddContextMenuAction(type, Resources.MenuItemMaxEntriesNumbers, ContextMenuActionType.MaxEntriesNumbers);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemCheckNewVersion, ContextMenuActionType.CheckNewVersion);
                        AddContextMenuAction(type, Resources.MenuItemAbout, ContextMenuActionType.About);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemExit, ContextMenuActionType.Exit);
                        break;
                    case ContextMenuType.Entry:
                        AddContextMenuAction(type, Resources.MenuItemCloseEntry, ContextMenuActionType.CloseEntry);
                        AddContextMenuAction(type, Resources.MenuItemCloseEntries, ContextMenuActionType.CloseEntries);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemEditEntry, ContextMenuActionType.EditEntry, Resources.note_edit);
                        AddContextMenuAction(type, Resources.MenuItemRemoveEntry, ContextMenuActionType.RemoveEntry, Resources.note_delete);
                        AddContextMenuAction(type, Resources.MenuItemEditDictionary, ContextMenuActionType.EditDictionary, Resources.data_edit);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemCopyEntry, ContextMenuActionType.CopyEntry, Resources.copy);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemSuspend, ContextMenuActionType.Suspend, Resources.hourglass);
                        AddContextMenuAction(type, Resources.MenuItemMaxEntriesNumbers, ContextMenuActionType.MaxEntriesNumbers);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("type");
                }
            }

            return _menus[type];
        }

        void OnContextMenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            if (_openedContextMenusCounter > 0)
            {
                _openedContextMenusCounter--;
            }
        }

        private void AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType)
        {
            AddContextMenuAction(type, text, actionType, null);
        }

        private void AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType, Image image)
        {
            ToolStripMenuItem item = new ToolStripMenuItem(text);
            item.Click += OnActionExecute;
            item.Tag = actionType;
            if (image != null)
            {
                // item.Image = image;
            }
            _menus[type].Items.Add(item);
        }

        private void AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType, bool hasCheckBox)
        {
            ToolStripMenuItem item = new ToolStripMenuItem(text);
            item.Click += OnActionExecute;
            item.Tag = actionType;
            item.CheckOnClick = hasCheckBox;
            _menus[type].Items.Add(item);
        }

        private void AddContextSeparator(ContextMenuType type)
        {
            _menus[type].Items.Add(new ToolStripSeparator());
        }

        void OnContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            _openedContextMenusCounter++;

            ContextMenuStrip menu = sender as ContextMenuStrip;
            if (menu == null)
            {
                throw new InvalidOperationException();
            }

            foreach (object item in menu.Items)
            {
                ToolStripMenuItem menuItem = item as ToolStripMenuItem;

                if (menuItem == null)
                {
                    continue;
                }

                switch ((ContextMenuActionType)menuItem.Tag)
                {
                    case ContextMenuActionType.CopyEntry:
                        menuItem.Enabled = true;
                        break;
                    case ContextMenuActionType.EditEntry:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.EditDictionary:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.RemoveEntry:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || !_root.FormsManager.IsEditingEntries();
                        break;
                    case ContextMenuActionType.CreateEntry:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.Settings:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.IsDoingSettings();
                        break;
                    case ContextMenuActionType.TurboMode:
                        menuItem.Enabled = _root.UserSettings.NewEntryMode.Value != NewEntryMode.OnDemand && !_root.Suspended;
                        menuItem.Checked = _root.TurboMode && menuItem.Enabled;
                        break;
                    case ContextMenuActionType.Exit:
                        break;
                    case ContextMenuActionType.DisplayEntry:
                        break;
                    case ContextMenuActionType.CloseEntry:
                        break;
                    case ContextMenuActionType.CloseEntries:
                        break;
                    case ContextMenuActionType.Suspend:
                        menuItem.Enabled = _root.UserSettings.NewEntryMode.Value != NewEntryMode.OnDemand;
                        PopulateSuspendMenuItem(menuItem);
                        break;
                    case ContextMenuActionType.MaxEntriesNumbers:
                        PopulateMaxEntriesMenuItem(menuItem);
                        break;
                    case ContextMenuActionType.About:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<AboutForm>() != null;
                        break;
                    case ContextMenuActionType.CheckNewVersion:
                        menuItem.Enabled = _root.FormsManager.Get<NewVersionForm>() == null;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void PopulateMaxEntriesMenuItem(ToolStripDropDownItem menuItem)
        {
            menuItem.DropDownItems.Clear();

            foreach (int maxEntriesNumber in Utilities.ParseToNumbers(_root.UserSettings.MaxEntriesNumbers.Value))
            {
                int number = maxEntriesNumber;

                ToolStripMenuItem item = new ToolStripMenuItem(number.ToString());
                item.Click += delegate
                                  {
                                      using (_root.UserSettings.Edit())
                                      {
                                          _root.UserSettings.MaxEntriesNumber.Value = number;
                                      }
                                  };
                if (_root.UserSettings.MaxEntriesNumber.Value == number)
                {
                    item.Checked = true;
                }

                menuItem.DropDownItems.Add(item);
            }
        }

        private void PopulateSuspendMenuItem(ToolStripDropDownItem menuItem)
        {
            menuItem.DropDownItems.Clear();

            if (menuItem.Enabled)
            {
                if (_root.Suspended)
                {
                    ToolStripMenuItem item = new ToolStripMenuItem(Resources.MenuItemResume);
                    item.Click += delegate { _root.Resume(); };
                    menuItem.DropDownItems.Add(item);

                    menuItem.DropDownItems.Add(new ToolStripSeparator());
                }

                foreach (int suspendInterval in Utilities.ParseToNumbers(_root.UserSettings.SuspendIntervals.Value))
                {
                    int interval = suspendInterval;

                    ToolStripMenuItem item = new ToolStripMenuItem(String.Format(Resources.SuspendInterval, suspendInterval));
                    item.Click += delegate { _root.Suspend(interval); };

                    menuItem.DropDownItems.Add(item);
                }
            }
        }

        void OnActionExecute(object sender, EventArgs e)
        {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
            if (menuItem == null)
            {
                throw new InvalidOperationException();
            }

            switch ((ContextMenuActionType)menuItem.Tag)
            {
                case ContextMenuActionType.EditEntry:
                    EditEntry();
                    break;
                case ContextMenuActionType.EditDictionary:
                    EditDictionary();
                    break;
                case ContextMenuActionType.RemoveEntry:
                    try
                    {
                        _entryDescriptor.Dictionary.UpdateEntry(EditedEntryDescriptor.CreateRemoved(_entryDescriptor.EntryId));
                    }
                    catch (DictionaryException exception)
                    {
                        Logging.Error(GetType(), exception, "Dictionary could not be saved.");

                        MessageBox.Show(Resources.DictionarySaveError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    break;
                case ContextMenuActionType.CreateEntry:
                    _root.CreateEntry();
                    break;
                case ContextMenuActionType.Settings:
                    _root.FormsManager.ShowSettingsForm();
                    break;
                case ContextMenuActionType.TurboMode:
                    _root.TurboMode = menuItem.Checked;
                    break;
                case ContextMenuActionType.Exit:
                    System.Windows.Forms.Application.Exit();
                    break;
                case ContextMenuActionType.DisplayEntry:
                    _root.DisplayEntry();
                    break;
                case ContextMenuActionType.CloseEntry:
                    _root.DisplayManager.CloseEntry(_entryDescriptor);
                    break;
                case ContextMenuActionType.CloseEntries:
                    _root.DisplayManager.CloseEntries();
                    break;
                case ContextMenuActionType.CopyEntry:
                    CopyEntry();
                    break;
                case ContextMenuActionType.Suspend:
                    break;
                case ContextMenuActionType.MaxEntriesNumbers:
                    break;
                case ContextMenuActionType.About:
                    _root.FormsManager.ShowAboutForm();
                    break;
                case ContextMenuActionType.CheckNewVersion:
                    OnlineVersionChecker.Check(_root, false);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void CopyEntry()
        {
            ClipboardEntries entries = new ClipboardEntries();
            entries.From = _entryDescriptor.Dictionary.Entries.From;
            entries.To = _entryDescriptor.Dictionary.Entries.To;
            entries.Items = new List<ClipboardEntries.ClipboardEntry>();

            ClipboardEntries.ClipboardEntry entry = new ClipboardEntries.ClipboardEntry();
            entry.From = _entryDescriptor.Entry.From;
            entry.To = _entryDescriptor.Entry.To;
            entry.Example = _entryDescriptor.Example;

            entries.Items.Add(entry);

            entries.Copy();
        }

        private void EditDictionary()
        {
            EntriesEditorForm form = new EntriesEditorForm(_root, _root.Dictionaries, _entryDescriptor.Dictionary);
            _root.FormsManager.Add(form);
            form.Show();
            form.Closed += delegate
                                            {
                                                _root.FormsManager.Remove(form);
                                            };
        }

        private void EditEntry()
        {
            Entry entry = _entryDescriptor.Dictionary.Entries.Items[_entryDescriptor.EntryId];

            List<EditedEntryDescriptor> entries = new List<EditedEntryDescriptor>();
            entries.Add(EditedEntryDescriptor.Create(entry, _entryDescriptor.Dictionary.GetEntryPriority(entry.Id)));

            AdvancedEntryEditorForm form = new AdvancedEntryEditorForm(_root, entries, _entryDescriptor.Dictionary);
            _root.FormsManager.Add(form);
            form.Show();
            form.Closed += delegate
                               {
                                   if (form.DialogResult == DialogResult.OK && form.Dictionary != null)
                                   {
                                       form.Dictionary.UpdateEntries(entries);
                                   }

                                   _root.FormsManager.Remove(form);
                               };
        }

        public void SetCurrentEntryDescriptor(EntryDescriptor descriptor)
        {
            _entryDescriptor = descriptor;
        }
    }
}
